@axiom-lattice/local-stores 3.1.6 → 3.1.9

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/index.mjs CHANGED
@@ -808,12 +808,20 @@ CREATE UNIQUE INDEX IF NOT EXISTS uq_lt_project_room_messages_idempotency
808
808
  ON lt_project_room_messages (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
809
809
  CREATE INDEX IF NOT EXISTS idx_lt_project_room_messages_room_created
810
810
  ON lt_project_room_messages (tenant_id, room_id, created_at DESC, id DESC);
811
+ CREATE TABLE IF NOT EXISTS lt_project_room_read_states (
812
+ tenant_id TEXT NOT NULL, room_id TEXT NOT NULL, user_id TEXT NOT NULL,
813
+ last_read_at TEXT NOT NULL, updated_at TEXT NOT NULL,
814
+ PRIMARY KEY (tenant_id, room_id, user_id)
815
+ );
816
+ CREATE INDEX IF NOT EXISTS idx_lt_project_room_read_states_user
817
+ ON lt_project_room_read_states (tenant_id, user_id);
811
818
  `;
812
819
  var tableColumns = {
813
820
  lt_project_rooms: ["id", "tenant_id", "workspace_id", "project_id", "type", "name", "created_at", "updated_at"],
814
821
  lt_project_memberships: ["id", "tenant_id", "project_id", "user_id", "role", "status", "joined_at", "updated_at"],
815
822
  lt_project_bot_memberships: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "assistant_id", "role", "title", "responsibility", "mention_name", "status", "room_thread_id", "joined_at", "updated_at"],
816
- lt_project_room_messages: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "author", "content", "mentions", "reply_to_message_id", "source", "source_id", "idempotency_key", "created_at"]
823
+ lt_project_room_messages: ["id", "tenant_id", "workspace_id", "project_id", "room_id", "author", "content", "mentions", "reply_to_message_id", "source", "source_id", "idempotency_key", "created_at"],
824
+ lt_project_room_read_states: ["tenant_id", "room_id", "user_id", "last_read_at", "updated_at"]
817
825
  };
818
826
  var indexes = {
819
827
  idx_lt_project_memberships_project: { table: "lt_project_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
@@ -822,13 +830,15 @@ var indexes = {
822
830
  idx_lt_project_bot_memberships_project: { table: "lt_project_bot_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
823
831
  idx_lt_project_bot_memberships_room: { table: "lt_project_bot_memberships", columns: ["tenant_id", "room_id"], unique: false, partial: false },
824
832
  uq_lt_project_room_messages_idempotency: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "idempotency_key"], unique: true, partial: true, sql: ["idempotency_key is not null"] },
825
- idx_lt_project_room_messages_room_created: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "created_at", "id"], unique: false, partial: false, sql: ["created_at desc", "id desc"] }
833
+ idx_lt_project_room_messages_room_created: { table: "lt_project_room_messages", columns: ["tenant_id", "room_id", "created_at", "id"], unique: false, partial: false, sql: ["created_at desc", "id desc"] },
834
+ idx_lt_project_room_read_states_user: { table: "lt_project_room_read_states", columns: ["tenant_id", "user_id"], unique: false, partial: false }
826
835
  };
827
836
  var uniqueConstraints = {
828
837
  lt_project_rooms: [["tenant_id", "id"], ["tenant_id", "project_id", "type"]],
829
838
  lt_project_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "user_id"]],
830
839
  lt_project_bot_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "assistant_id"]],
831
- lt_project_room_messages: [["tenant_id", "id"]]
840
+ lt_project_room_messages: [["tenant_id", "id"]],
841
+ lt_project_room_read_states: [["tenant_id", "room_id", "user_id"]]
832
842
  };
833
843
  var checkConstraints = {
834
844
  lt_project_rooms: [{ name: "type", fragment: "type='main'" }],
@@ -840,7 +850,8 @@ var checkConstraints = {
840
850
  { name: "role", fragment: "role in ('coordinator','specialist')" },
841
851
  { name: "status", fragment: "status in ('active','paused','removed')" }
842
852
  ],
843
- lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }]
853
+ lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }],
854
+ lt_project_room_read_states: []
844
855
  };
845
856
  function rows(db, sql) {
846
857
  const result = db.getRawDb().exec(sql);
@@ -912,10 +923,10 @@ function validateProjectRoomSchema(db) {
912
923
  }
913
924
  for (const [name, expected] of Object.entries(indexes)) {
914
925
  const listed = rows(db, `PRAGMA index_list(${expected.table})`).find((index) => index.name === name);
915
- const columns5 = rows(db, `PRAGMA index_info(${name})`).map((column) => column.name);
926
+ const columns6 = rows(db, `PRAGMA index_info(${name})`).map((column) => column.name);
916
927
  const definition = rows(db, `SELECT sql FROM sqlite_master WHERE type = 'index' AND name = '${name}'`)[0]?.sql;
917
928
  const normalized = typeof definition === "string" ? definition.toLowerCase().replace(/\s+/g, " ") : "";
918
- if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) || columns5.join(",") !== expected.columns.join(",") || expected.sql?.some((fragment) => !normalized.includes(fragment))) {
929
+ if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) || columns6.join(",") !== expected.columns.join(",") || expected.sql?.some((fragment) => !normalized.includes(fragment))) {
919
930
  throw new Error(`Invalid project room schema: index '${name}' is incompatible`);
920
931
  }
921
932
  }
@@ -928,6 +939,30 @@ function ensureProjectRoomSchema(db) {
928
939
  const applied = hasMigrationTable ? rows(db, `SELECT name, version FROM lattice_schema_migrations WHERE store_name = 'project_room' AND name = '${migrationName}'`)[0] : void 0;
929
940
  if (applied) {
930
941
  if (applied.version !== 1) throw new Error(`Invalid project room schema migration version: ${String(applied.version)}`);
942
+ const hasReadStates = rows(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'lt_project_room_read_states'").length > 0;
943
+ if (!hasReadStates) {
944
+ raw.exec("BEGIN IMMEDIATE");
945
+ try {
946
+ raw.exec(`CREATE TABLE IF NOT EXISTS lt_project_room_read_states (
947
+ tenant_id TEXT NOT NULL, room_id TEXT NOT NULL, user_id TEXT NOT NULL,
948
+ last_read_at TEXT NOT NULL, updated_at TEXT NOT NULL,
949
+ PRIMARY KEY (tenant_id, room_id, user_id)
950
+ ); CREATE INDEX IF NOT EXISTS idx_lt_project_room_read_states_user
951
+ ON lt_project_room_read_states (tenant_id, user_id);`);
952
+ raw.run(
953
+ "INSERT INTO lattice_schema_migrations (store_name, name, version, applied_at) VALUES (?, ?, ?, ?)",
954
+ ["project_room", "create_project_room_read_states", 2, (/* @__PURE__ */ new Date()).toISOString()]
955
+ );
956
+ raw.exec("COMMIT");
957
+ db.save();
958
+ } catch (error) {
959
+ try {
960
+ raw.exec("ROLLBACK");
961
+ } catch {
962
+ }
963
+ throw error;
964
+ }
965
+ }
931
966
  validateProjectRoomSchema(db);
932
967
  marked[schemaMarker] = true;
933
968
  return;
@@ -1346,6 +1381,22 @@ function mapRow4(row) {
1346
1381
  createdAt
1347
1382
  };
1348
1383
  }
1384
+ function mapRowsTolerant(rows2, scope) {
1385
+ const messages = [];
1386
+ for (const row of rows2) {
1387
+ try {
1388
+ messages.push(mapRow4(row));
1389
+ } catch (error) {
1390
+ console.warn("[ProjectRoomMessageStore] Skipping invalid message row", {
1391
+ tenantId: scope.tenantId,
1392
+ roomId: scope.roomId,
1393
+ rowId: typeof row.id === "string" ? row.id : void 0,
1394
+ reason: error instanceof Error ? error.message : String(error)
1395
+ });
1396
+ }
1397
+ }
1398
+ return messages;
1399
+ }
1349
1400
  var LocalProjectRoomMessageStore = class {
1350
1401
  constructor(db) {
1351
1402
  this.db = db;
@@ -1361,12 +1412,24 @@ var LocalProjectRoomMessageStore = class {
1361
1412
  if (input.before && !validDate(input.before.createdAt)) throw new RangeError("Project room message cursor date is invalid");
1362
1413
  const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
1363
1414
  const rows2 = input.before ? this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? AND (created_at < ? OR (created_at = ? AND id < ?)) ORDER BY created_at DESC, id DESC LIMIT ?`).all(input.tenantId, input.roomId, input.before.createdAt.toISOString(), input.before.createdAt.toISOString(), input.before.id, limit) : this.db.prepare(`SELECT ${columns4} FROM lt_project_room_messages WHERE tenant_id = ? AND room_id = ? ORDER BY created_at DESC, id DESC LIMIT ?`).all(input.tenantId, input.roomId, limit);
1364
- return rows2.map(mapRow4);
1415
+ return mapRowsTolerant(rows2, { tenantId: input.tenantId, roomId: input.roomId });
1365
1416
  }
1366
1417
  async findById(tenantId, id) {
1367
1418
  const row = this.byId(tenantId, id);
1368
1419
  return row ? mapRow4(row) : null;
1369
1420
  }
1421
+ async countAfter(input) {
1422
+ if (!validDate(input.after)) throw new RangeError("Project room message count horizon date is invalid");
1423
+ const rows2 = this.db.prepare(
1424
+ `SELECT author FROM lt_project_room_messages
1425
+ WHERE tenant_id = ? AND room_id = ? AND created_at > ?`
1426
+ ).all(input.tenantId, input.roomId, input.after.toISOString());
1427
+ return rows2.filter((row) => {
1428
+ if (input.excludeAuthorUserId === void 0) return true;
1429
+ const parsed = parseJson(row.author);
1430
+ return !(record(parsed) && parsed.type === "human" && parsed.userId === input.excludeAuthorUserId);
1431
+ }).length;
1432
+ }
1370
1433
  insert(input, idempotent) {
1371
1434
  return transaction(this.db, () => {
1372
1435
  if (idempotent && input.idempotencyKey) {
@@ -1397,6 +1460,50 @@ var LocalProjectRoomMessageStore = class {
1397
1460
  }
1398
1461
  };
1399
1462
 
1463
+ // src/stores/LocalProjectRoomReadStateStore.ts
1464
+ var columns5 = "tenant_id, room_id, user_id, last_read_at, updated_at";
1465
+ function mapRow5(row) {
1466
+ const lastReadAt = new Date(row.last_read_at);
1467
+ const updatedAt = new Date(row.updated_at);
1468
+ if (Number.isNaN(lastReadAt.getTime()) || Number.isNaN(updatedAt.getTime())) throw new Error("Invalid project room read state row");
1469
+ return { tenantId: row.tenant_id, roomId: row.room_id, userId: row.user_id, lastReadAt, updatedAt };
1470
+ }
1471
+ var LocalProjectRoomReadStateStore = class {
1472
+ /** Creates a store over a managed local database wrapper. */
1473
+ constructor(db) {
1474
+ this.db = db;
1475
+ ensureProjectRoomSchema(db);
1476
+ }
1477
+ /** Returns the user's read marker for a room, or null when never reported. */
1478
+ async get(tenantId, roomId, userId) {
1479
+ const row = this.byKey(tenantId, roomId, userId);
1480
+ return row ? mapRow5(row) : null;
1481
+ }
1482
+ /** Upserts the marker monotonically (an older report never regresses the horizon). */
1483
+ async markRead(input) {
1484
+ if (!validDate(input.lastReadAt)) throw new RangeError("Project room read state lastReadAt is invalid");
1485
+ return transaction(this.db, () => {
1486
+ const existing = this.byKey(input.tenantId, input.roomId, input.userId);
1487
+ const lastReadAt = existing !== void 0 && new Date(existing.last_read_at).getTime() > input.lastReadAt.getTime() ? new Date(existing.last_read_at) : input.lastReadAt;
1488
+ write(
1489
+ this.db,
1490
+ `INSERT INTO lt_project_room_read_states (${columns5})
1491
+ VALUES (?, ?, ?, ?, ?)
1492
+ ON CONFLICT (tenant_id, room_id, user_id) DO UPDATE
1493
+ SET last_read_at = excluded.last_read_at, updated_at = excluded.updated_at`,
1494
+ [input.tenantId, input.roomId, input.userId, lastReadAt.toISOString(), timestamp()]
1495
+ );
1496
+ const row = this.byKey(input.tenantId, input.roomId, input.userId);
1497
+ return mapRow5(row);
1498
+ });
1499
+ }
1500
+ byKey(tenantId, roomId, userId) {
1501
+ return this.db.prepare(
1502
+ `SELECT ${columns5} FROM lt_project_room_read_states WHERE tenant_id = ? AND room_id = ? AND user_id = ?`
1503
+ ).get(tenantId, roomId, userId);
1504
+ }
1505
+ };
1506
+
1400
1507
  // src/stores/LocalUserStore.ts
1401
1508
  var DDL5 = `
1402
1509
  CREATE TABLE IF NOT EXISTS lt_users (
@@ -2224,6 +2331,18 @@ function mapRowToEntry3(row) {
2224
2331
  throw new Error("Failed to decrypt MCP server configuration");
2225
2332
  }
2226
2333
  }
2334
+ if (config.headers && row.is_env_encrypted) {
2335
+ try {
2336
+ const decryptedHeaders = {};
2337
+ for (const [key, value] of Object.entries(config.headers)) {
2338
+ decryptedHeaders[key] = decrypt2(value);
2339
+ }
2340
+ config.headers = decryptedHeaders;
2341
+ } catch (error) {
2342
+ console.error("Failed to decrypt MCP server headers:", error);
2343
+ throw new Error("Failed to decrypt MCP server configuration");
2344
+ }
2345
+ }
2227
2346
  return {
2228
2347
  id: row.id,
2229
2348
  tenantId: row.tenant_id,
@@ -2249,6 +2368,14 @@ function encryptEnvInConfig(config) {
2249
2368
  configCopy.env = encryptedEnv;
2250
2369
  isEnvEncrypted = true;
2251
2370
  }
2371
+ if (configCopy.headers && Object.keys(configCopy.headers).length > 0) {
2372
+ const encryptedHeaders = {};
2373
+ for (const [key, value] of Object.entries(configCopy.headers)) {
2374
+ encryptedHeaders[key] = encrypt2(value);
2375
+ }
2376
+ configCopy.headers = encryptedHeaders;
2377
+ isEnvEncrypted = true;
2378
+ }
2252
2379
  return { config: configCopy, isEnvEncrypted };
2253
2380
  }
2254
2381
 
@@ -3562,7 +3689,7 @@ var LocalChannelInstallationStore = class {
3562
3689
  const row = this.db.prepare(
3563
3690
  `SELECT * FROM lt_channel_installations WHERE id = ?`
3564
3691
  ).get(installationId);
3565
- return row ? mapRow5(row) : null;
3692
+ return row ? mapRow6(row) : null;
3566
3693
  }
3567
3694
  async getInstallationsByTenant(tenantId, channel) {
3568
3695
  let rows2;
@@ -3575,7 +3702,7 @@ var LocalChannelInstallationStore = class {
3575
3702
  `SELECT * FROM lt_channel_installations WHERE tenant_id = ? ORDER BY created_at DESC`
3576
3703
  ).all(tenantId);
3577
3704
  }
3578
- return rows2.map(mapRow5);
3705
+ return rows2.map(mapRow6);
3579
3706
  }
3580
3707
  async getAllInstallations(channel) {
3581
3708
  let rows2;
@@ -3588,7 +3715,7 @@ var LocalChannelInstallationStore = class {
3588
3715
  `SELECT * FROM lt_channel_installations ORDER BY created_at DESC`
3589
3716
  ).all();
3590
3717
  }
3591
- return rows2.map(mapRow5);
3718
+ return rows2.map(mapRow6);
3592
3719
  }
3593
3720
  async createInstallation(tenantId, installationId, data) {
3594
3721
  const now = nowISO();
@@ -3664,7 +3791,7 @@ var LocalChannelInstallationStore = class {
3664
3791
  return result.changes > 0;
3665
3792
  }
3666
3793
  };
3667
- function mapRow5(row) {
3794
+ function mapRow6(row) {
3668
3795
  return {
3669
3796
  id: row.id,
3670
3797
  tenantId: row.tenant_id,
@@ -3689,6 +3816,7 @@ CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
3689
3816
  tenant_id TEXT NOT NULL,
3690
3817
  project_id TEXT,
3691
3818
  assistant_ids TEXT,
3819
+ grants TEXT,
3692
3820
  label TEXT,
3693
3821
  enabled INTEGER NOT NULL DEFAULT 1,
3694
3822
  created_at TEXT NOT NULL,
@@ -3698,6 +3826,15 @@ CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
3698
3826
  function generateApiKey() {
3699
3827
  return `a2a_${randomUUID3().replace(/-/g, "")}`;
3700
3828
  }
3829
+ function parseGrants(raw) {
3830
+ if (!raw) return void 0;
3831
+ try {
3832
+ const parsed = JSON.parse(raw);
3833
+ return Array.isArray(parsed) ? parsed : void 0;
3834
+ } catch {
3835
+ return void 0;
3836
+ }
3837
+ }
3701
3838
  function parseAssistantIds(raw) {
3702
3839
  if (!raw) return void 0;
3703
3840
  try {
@@ -3712,6 +3849,7 @@ var LocalA2AApiKeyStore = class {
3712
3849
  this.db = db;
3713
3850
  ensureTable(db, DDL16);
3714
3851
  this.ensureColumn("lt_a2a_api_keys", "assistant_ids", "assistant_ids TEXT");
3852
+ this.ensureColumn("lt_a2a_api_keys", "grants", "grants TEXT");
3715
3853
  this.repairManagementIds();
3716
3854
  }
3717
3855
  /** Add a column if it does not exist (SQLite version compatible). */
@@ -3772,9 +3910,9 @@ var LocalA2AApiKeyStore = class {
3772
3910
  const key = generateApiKey();
3773
3911
  const now = nowISO();
3774
3912
  this.db.prepare(
3775
- `INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, label, created_at, updated_at)
3776
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
3777
- ).run(id, encrypt3(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.label || null, now, now);
3913
+ `INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, grants, label, created_at, updated_at)
3914
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
3915
+ ).run(id, encrypt3(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.grants ? JSON.stringify(input.grants) : null, input.label || null, now, now);
3778
3916
  const record2 = {
3779
3917
  id,
3780
3918
  key,
@@ -3806,6 +3944,38 @@ var LocalA2AApiKeyStore = class {
3806
3944
  if (!row) throw new Error(`A2A API key not found: ${id}`);
3807
3945
  return mapRowToRecord(row);
3808
3946
  }
3947
+ async update(id, input) {
3948
+ const sets = [];
3949
+ const values = [];
3950
+ if (input.label !== void 0) {
3951
+ sets.push("label = ?");
3952
+ values.push(input.label || null);
3953
+ }
3954
+ if (input.projectId !== void 0) {
3955
+ sets.push("project_id = ?");
3956
+ values.push(input.projectId);
3957
+ }
3958
+ if (input.assistantIds !== void 0) {
3959
+ sets.push("assistant_ids = ?");
3960
+ values.push(input.assistantIds ? JSON.stringify(input.assistantIds) : null);
3961
+ }
3962
+ if (input.grants !== void 0) {
3963
+ sets.push("grants = ?");
3964
+ values.push(input.grants ? JSON.stringify(input.grants) : null);
3965
+ }
3966
+ if (sets.length === 0) {
3967
+ const row2 = this.db.prepare("SELECT * FROM lt_a2a_api_keys WHERE id = ?").get(id);
3968
+ if (!row2) throw new Error(`A2A API key not found: ${id}`);
3969
+ return mapRowToRecord(row2);
3970
+ }
3971
+ sets.push("updated_at = ?");
3972
+ values.push(nowISO());
3973
+ values.push(id);
3974
+ this.db.prepare(`UPDATE lt_a2a_api_keys SET ${sets.join(", ")} WHERE id = ?`).run(...values);
3975
+ const row = this.db.prepare("SELECT * FROM lt_a2a_api_keys WHERE id = ?").get(id);
3976
+ if (!row) throw new Error(`A2A API key not found: ${id}`);
3977
+ return mapRowToRecord(row);
3978
+ }
3809
3979
  async rotate(id) {
3810
3980
  const key = generateApiKey();
3811
3981
  const now = nowISO();
@@ -3854,6 +4024,7 @@ function mapRowToRecord(row) {
3854
4024
  tenantId: row.tenant_id,
3855
4025
  projectId: row.project_id,
3856
4026
  assistantIds: parseAssistantIds(row.assistant_ids),
4027
+ grants: parseGrants(row.grants),
3857
4028
  label: row.label || void 0,
3858
4029
  enabled: row.enabled === 1,
3859
4030
  createdAt: parseISO(row.created_at),
@@ -3861,13 +4032,103 @@ function mapRowToRecord(row) {
3861
4032
  };
3862
4033
  }
3863
4034
 
4035
+ // src/stores/LocalOpenAuditStore.ts
4036
+ import { randomUUID as randomUUID4 } from "crypto";
4037
+ var DDL17 = `
4038
+ CREATE TABLE IF NOT EXISTS lt_open_audit (
4039
+ id TEXT PRIMARY KEY,
4040
+ tenant_id TEXT NOT NULL,
4041
+ credential_id TEXT NOT NULL,
4042
+ credential_kind TEXT NOT NULL,
4043
+ project_id TEXT NOT NULL,
4044
+ domain TEXT NOT NULL,
4045
+ action TEXT NOT NULL,
4046
+ args TEXT,
4047
+ status TEXT NOT NULL,
4048
+ error_code TEXT,
4049
+ duration_ms INTEGER NOT NULL,
4050
+ created_at TEXT NOT NULL
4051
+ );
4052
+ `;
4053
+ var LocalOpenAuditStore = class {
4054
+ constructor(db) {
4055
+ this.db = db;
4056
+ ensureTable(db, DDL17);
4057
+ }
4058
+ async query(params) {
4059
+ const clauses = ["tenant_id = ?"];
4060
+ const values = [params.tenantId];
4061
+ if (params.credentialId) {
4062
+ clauses.push("credential_id = ?");
4063
+ values.push(params.credentialId);
4064
+ }
4065
+ if (params.domain) {
4066
+ clauses.push("domain = ?");
4067
+ values.push(params.domain);
4068
+ }
4069
+ if (params.status) {
4070
+ clauses.push("status = ?");
4071
+ values.push(params.status);
4072
+ }
4073
+ if (params.from) {
4074
+ clauses.push("created_at >= ?");
4075
+ values.push(params.from);
4076
+ }
4077
+ if (params.to) {
4078
+ clauses.push("created_at < ?");
4079
+ values.push(params.to);
4080
+ }
4081
+ const limit = params.limit ?? 100;
4082
+ const offset = params.offset ?? 0;
4083
+ values.push(limit, offset);
4084
+ const rows2 = this.db.prepare(
4085
+ `SELECT * FROM lt_open_audit WHERE ${clauses.join(" AND ")}
4086
+ ORDER BY created_at DESC LIMIT ? OFFSET ?`
4087
+ ).all(...values);
4088
+ return rows2.map((row) => ({
4089
+ id: row.id,
4090
+ tenantId: row.tenant_id,
4091
+ credentialId: row.credential_id,
4092
+ credentialKind: row.credential_kind,
4093
+ projectId: row.project_id,
4094
+ domain: row.domain,
4095
+ action: row.action,
4096
+ args: row.args ? JSON.parse(row.args) : void 0,
4097
+ status: row.status,
4098
+ errorCode: row.error_code ?? void 0,
4099
+ durationMs: row.duration_ms,
4100
+ createdAt: parseISO(row.created_at)
4101
+ }));
4102
+ }
4103
+ async append(record2) {
4104
+ this.db.prepare(
4105
+ `INSERT INTO lt_open_audit
4106
+ (id, tenant_id, credential_id, credential_kind, project_id, domain, action, args, status, error_code, duration_ms, created_at)
4107
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
4108
+ ).run(
4109
+ randomUUID4(),
4110
+ record2.tenantId,
4111
+ record2.credentialId,
4112
+ record2.credentialKind,
4113
+ record2.projectId,
4114
+ record2.domain,
4115
+ record2.action,
4116
+ record2.args === void 0 ? null : JSON.stringify(record2.args),
4117
+ record2.status,
4118
+ record2.errorCode ?? null,
4119
+ Math.max(0, Math.round(record2.durationMs)),
4120
+ nowISO()
4121
+ );
4122
+ }
4123
+ };
4124
+
3864
4125
  // src/stores/LocalThreadMessageQueueStore.ts
3865
4126
  import {
3866
4127
  parseQueuedExecutionMode,
3867
4128
  parseTrustedRunContext
3868
4129
  } from "@axiom-lattice/protocols";
3869
- import { randomUUID as randomUUID4 } from "crypto";
3870
- var DDL17 = `
4130
+ import { randomUUID as randomUUID5 } from "crypto";
4131
+ var DDL18 = `
3871
4132
  CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
3872
4133
  id TEXT PRIMARY KEY,
3873
4134
  thread_id TEXT NOT NULL,
@@ -3893,9 +4154,9 @@ var LocalThreadMessageQueueStore = class {
3893
4154
  constructor(db) {
3894
4155
  this.capacityTail = Promise.resolve();
3895
4156
  this.db = db;
3896
- ensureTable(db, DDL17);
3897
- const columns5 = db.prepare("PRAGMA table_info(lt_thread_message_queue)").all();
3898
- const names = new Set(columns5.map(({ name }) => name));
4157
+ ensureTable(db, DDL18);
4158
+ const columns6 = db.prepare("PRAGMA table_info(lt_thread_message_queue)").all();
4159
+ const names = new Set(columns6.map(({ name }) => name));
3899
4160
  if (!names.has("trusted_run_context")) {
3900
4161
  db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN trusted_run_context TEXT");
3901
4162
  }
@@ -3906,7 +4167,7 @@ var LocalThreadMessageQueueStore = class {
3906
4167
  async addMessage(params) {
3907
4168
  const trusted = validateQueueTrust(params);
3908
4169
  const now = nowISO();
3909
- const id = params.id || randomUUID4();
4170
+ const id = params.id || randomUUID5();
3910
4171
  const seqRow = this.db.prepare(
3911
4172
  `SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
3912
4173
  ).get(params.threadId);
@@ -3949,7 +4210,7 @@ var LocalThreadMessageQueueStore = class {
3949
4210
  projectId: params.projectId
3950
4211
  };
3951
4212
  const filter = scopeClause(scope);
3952
- const id = params.id || randomUUID4();
4213
+ const id = params.id || randomUUID5();
3953
4214
  const limit = maxSize === Infinity ? -1 : maxSize;
3954
4215
  const result = this.db.prepare(
3955
4216
  `INSERT INTO lt_thread_message_queue (id, thread_id, tenant_id, assistant_id, workspace_id, project_id, message_content, message_type, sequence_order, priority, command, custom_run_config, trusted_run_context, execution_mode, created_at)
@@ -3987,7 +4248,7 @@ var LocalThreadMessageQueueStore = class {
3987
4248
  async addMessageAtHead(params) {
3988
4249
  const trusted = validateQueueTrust(params);
3989
4250
  const now = nowISO();
3990
- const id = params.id || randomUUID4();
4251
+ const id = params.id || randomUUID5();
3991
4252
  const seqRow = this.db.prepare(
3992
4253
  `SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
3993
4254
  ).get(params.threadId);
@@ -4106,7 +4367,7 @@ function rowToMessage(row) {
4106
4367
  }
4107
4368
 
4108
4369
  // src/stores/LocalSkillStore.ts
4109
- var DDL18 = `
4370
+ var DDL19 = `
4110
4371
  CREATE TABLE IF NOT EXISTS lt_skills (
4111
4372
  id TEXT NOT NULL,
4112
4373
  tenant_id TEXT NOT NULL,
@@ -4125,7 +4386,7 @@ CREATE TABLE IF NOT EXISTS lt_skills (
4125
4386
  var LocalSkillStore = class {
4126
4387
  constructor(db) {
4127
4388
  this.db = db;
4128
- ensureTable(db, DDL18);
4389
+ ensureTable(db, DDL19);
4129
4390
  }
4130
4391
  async getAllSkills(tenantId, _context) {
4131
4392
  const rows2 = this.db.prepare(
@@ -4275,7 +4536,7 @@ function mapRowToSkill(row) {
4275
4536
  }
4276
4537
 
4277
4538
  // src/stores/LocalScheduleStorage.ts
4278
- var DDL19 = `
4539
+ var DDL20 = `
4279
4540
  CREATE TABLE IF NOT EXISTS lt_scheduled_tasks (
4280
4541
  task_id TEXT PRIMARY KEY,
4281
4542
  task_type TEXT NOT NULL,
@@ -4308,7 +4569,7 @@ CREATE INDEX IF NOT EXISTS idx_lt_st_tenant ON lt_scheduled_tasks(tenant_id);
4308
4569
  var LocalScheduleStorage = class {
4309
4570
  constructor(db) {
4310
4571
  this.db = db;
4311
- ensureTable(db, DDL19);
4572
+ ensureTable(db, DDL20);
4312
4573
  }
4313
4574
  async save(task) {
4314
4575
  this.db.prepare(
@@ -4602,7 +4863,7 @@ function mapRowToTask(row) {
4602
4863
  }
4603
4864
 
4604
4865
  // src/stores/LocalTaskStore.ts
4605
- import { randomUUID as randomUUID5 } from "crypto";
4866
+ import { randomUUID as randomUUID6 } from "crypto";
4606
4867
  var MAX_CANONICAL_TASK_TIME = "9999-12-31T23:59:59.999Z";
4607
4868
  var TASK_STATUSES = /* @__PURE__ */ new Set([
4608
4869
  "pending",
@@ -4618,7 +4879,7 @@ var NEXT_UPDATED_AT_SQL = `updated_at = CASE
4618
4879
  THEN strftime('%Y-%m-%dT%H:%M:%fZ', updated_at, '+0.001 seconds')
4619
4880
  ELSE ?
4620
4881
  END`;
4621
- var DDL20 = `
4882
+ var DDL21 = `
4622
4883
  CREATE TABLE IF NOT EXISTS lt_tasks (
4623
4884
  id TEXT NOT NULL,
4624
4885
  tenant_id TEXT NOT NULL,
@@ -4691,7 +4952,7 @@ function assertPage(limit, offset = 0) {
4691
4952
  var LocalTaskStore = class {
4692
4953
  constructor(db) {
4693
4954
  this.db = db;
4694
- ensureTable(db, DDL20);
4955
+ ensureTable(db, DDL21);
4695
4956
  this.ensureColumn("lt_tasks", "require_review", "require_review INTEGER DEFAULT 0");
4696
4957
  this.ensureColumn("lt_tasks", "dependencies", "dependencies TEXT");
4697
4958
  this.ensureColumn("lt_tasks", "result", "result TEXT");
@@ -4708,7 +4969,7 @@ var LocalTaskStore = class {
4708
4969
  }
4709
4970
  }
4710
4971
  async create(params) {
4711
- const id = params.id ?? randomUUID5();
4972
+ const id = params.id ?? randomUUID6();
4712
4973
  const now = nowISO();
4713
4974
  this.db.prepare(
4714
4975
  `INSERT INTO lt_tasks (id, tenant_id, owner_type, owner_id, title, description, status, priority, due_date, metadata, parent_id, source_id, context, require_review, dependencies, result, failure_reason, workspace_id, project_id, files, created_at, updated_at)
@@ -5368,7 +5629,7 @@ var PROJECT_LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set([
5368
5629
  "cancelled",
5369
5630
  "reassigned"
5370
5631
  ]);
5371
- var DDL21 = `
5632
+ var DDL22 = `
5372
5633
  CREATE TABLE IF NOT EXISTS lt_task_work_items (
5373
5634
  id TEXT NOT NULL,
5374
5635
  tenant_id TEXT NOT NULL,
@@ -5442,7 +5703,7 @@ function assertLifecyclePage(query) {
5442
5703
  var LocalTaskWorkItemStore = class {
5443
5704
  constructor(db) {
5444
5705
  this.db = db;
5445
- ensureTable(db, DDL21);
5706
+ ensureTable(db, DDL22);
5446
5707
  this.ensureColumn("lt_task_work_items", "workspace_id", "workspace_id TEXT");
5447
5708
  this.ensureColumn("lt_task_work_items", "project_id", "project_id TEXT");
5448
5709
  this.ensureColumn("lt_task_work_items", "event_key", "event_key TEXT");
@@ -5638,8 +5899,8 @@ var LocalTaskWorkItemStore = class {
5638
5899
  };
5639
5900
 
5640
5901
  // src/stores/LocalCapabilityBundleStore.ts
5641
- import { randomUUID as randomUUID6 } from "crypto";
5642
- var DDL22 = `CREATE TABLE IF NOT EXISTS lt_capability_bundles (id TEXT NOT NULL, tenant_id TEXT NOT NULL, bundle_key TEXT NOT NULL, name TEXT NOT NULL, description TEXT, capabilities TEXT NOT NULL DEFAULT '[]', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (tenant_id, id), UNIQUE (tenant_id, bundle_key)); CREATE INDEX IF NOT EXISTS idx_lt_capability_bundles_tenant ON lt_capability_bundles(tenant_id);`;
5902
+ import { randomUUID as randomUUID7 } from "crypto";
5903
+ var DDL23 = `CREATE TABLE IF NOT EXISTS lt_capability_bundles (id TEXT NOT NULL, tenant_id TEXT NOT NULL, bundle_key TEXT NOT NULL, name TEXT NOT NULL, description TEXT, capabilities TEXT NOT NULL DEFAULT '[]', created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (tenant_id, id), UNIQUE (tenant_id, bundle_key)); CREATE INDEX IF NOT EXISTS idx_lt_capability_bundles_tenant ON lt_capability_bundles(tenant_id);`;
5643
5904
  var duplicateMessage = "Capability bundle key already exists for tenant";
5644
5905
  function map(row) {
5645
5906
  return { id: row.id, tenantId: row.tenant_id, key: row.bundle_key, name: row.name, description: row.description ?? void 0, capabilities: JSON.parse(row.capabilities), createdAt: parseISO(row.created_at).toISOString(), updatedAt: parseISO(row.updated_at).toISOString() };
@@ -5660,7 +5921,7 @@ var LocalCapabilityBundleStore = class {
5660
5921
  */
5661
5922
  constructor(db) {
5662
5923
  this.db = db;
5663
- ensureTable(db, DDL22);
5924
+ ensureTable(db, DDL23);
5664
5925
  }
5665
5926
  async listByTenant(tenantId) {
5666
5927
  return this.db.prepare("SELECT * FROM lt_capability_bundles WHERE tenant_id = ? ORDER BY created_at").all(tenantId).map(map);
@@ -5677,7 +5938,7 @@ var LocalCapabilityBundleStore = class {
5677
5938
  }
5678
5939
  async create(tenantId, input) {
5679
5940
  const now = nextTimestamp();
5680
- const id = randomUUID6();
5941
+ const id = randomUUID7();
5681
5942
  try {
5682
5943
  this.db.prepare("INSERT INTO lt_capability_bundles (id, tenant_id, bundle_key, name, description, capabilities, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(id, tenantId, input.key, input.name, input.description ?? null, JSON.stringify(input.capabilities), now, now);
5683
5944
  } catch (error) {
@@ -5746,6 +6007,7 @@ async function createLocalStoreConfig(options = {}) {
5746
6007
  projectMembership: new LocalProjectMembershipStore(db),
5747
6008
  projectBotMembership: new LocalProjectBotMembershipStore(db),
5748
6009
  projectRoomMessage: new LocalProjectRoomMessageStore(db),
6010
+ projectRoomReadState: new LocalProjectRoomReadStateStore(db),
5749
6011
  user: new LocalUserStore(db),
5750
6012
  tenant: new LocalTenantStore(db),
5751
6013
  userTenantLink: new LocalUserTenantLinkStore(db),
@@ -5758,6 +6020,7 @@ async function createLocalStoreConfig(options = {}) {
5758
6020
  channelBinding: new LocalChannelBindingStore(db),
5759
6021
  channelInstallation: new LocalChannelInstallationStore(db),
5760
6022
  a2aApiKey: new LocalA2AApiKeyStore(db),
6023
+ openAudit: new LocalOpenAuditStore(db),
5761
6024
  threadMessageQueue: new LocalThreadMessageQueueStore(db),
5762
6025
  skill: new LocalSkillStore(db),
5763
6026
  schedule: new LocalScheduleStorage(db),
@@ -5819,9 +6082,11 @@ export {
5819
6082
  LocalEvalStore,
5820
6083
  LocalMcpServerConfigStore,
5821
6084
  LocalMetricsServerConfigStore,
6085
+ LocalOpenAuditStore,
5822
6086
  LocalProjectBotMembershipStore,
5823
6087
  LocalProjectMembershipStore,
5824
6088
  LocalProjectRoomMessageStore,
6089
+ LocalProjectRoomReadStateStore,
5825
6090
  LocalProjectRoomStore,
5826
6091
  LocalProjectStore,
5827
6092
  LocalScheduleStorage,