@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.js CHANGED
@@ -44,9 +44,11 @@ __export(index_exports, {
44
44
  LocalEvalStore: () => LocalEvalStore,
45
45
  LocalMcpServerConfigStore: () => LocalMcpServerConfigStore,
46
46
  LocalMetricsServerConfigStore: () => LocalMetricsServerConfigStore,
47
+ LocalOpenAuditStore: () => LocalOpenAuditStore,
47
48
  LocalProjectBotMembershipStore: () => LocalProjectBotMembershipStore,
48
49
  LocalProjectMembershipStore: () => LocalProjectMembershipStore,
49
50
  LocalProjectRoomMessageStore: () => LocalProjectRoomMessageStore,
51
+ LocalProjectRoomReadStateStore: () => LocalProjectRoomReadStateStore,
50
52
  LocalProjectRoomStore: () => LocalProjectRoomStore,
51
53
  LocalProjectStore: () => LocalProjectStore,
52
54
  LocalScheduleStorage: () => LocalScheduleStorage,
@@ -886,12 +888,20 @@ CREATE UNIQUE INDEX IF NOT EXISTS uq_lt_project_room_messages_idempotency
886
888
  ON lt_project_room_messages (tenant_id, room_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
887
889
  CREATE INDEX IF NOT EXISTS idx_lt_project_room_messages_room_created
888
890
  ON lt_project_room_messages (tenant_id, room_id, created_at DESC, id DESC);
891
+ CREATE TABLE IF NOT EXISTS lt_project_room_read_states (
892
+ tenant_id TEXT NOT NULL, room_id TEXT NOT NULL, user_id TEXT NOT NULL,
893
+ last_read_at TEXT NOT NULL, updated_at TEXT NOT NULL,
894
+ PRIMARY KEY (tenant_id, room_id, user_id)
895
+ );
896
+ CREATE INDEX IF NOT EXISTS idx_lt_project_room_read_states_user
897
+ ON lt_project_room_read_states (tenant_id, user_id);
889
898
  `;
890
899
  var tableColumns = {
891
900
  lt_project_rooms: ["id", "tenant_id", "workspace_id", "project_id", "type", "name", "created_at", "updated_at"],
892
901
  lt_project_memberships: ["id", "tenant_id", "project_id", "user_id", "role", "status", "joined_at", "updated_at"],
893
902
  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"],
894
- 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"]
903
+ 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"],
904
+ lt_project_room_read_states: ["tenant_id", "room_id", "user_id", "last_read_at", "updated_at"]
895
905
  };
896
906
  var indexes = {
897
907
  idx_lt_project_memberships_project: { table: "lt_project_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
@@ -900,13 +910,15 @@ var indexes = {
900
910
  idx_lt_project_bot_memberships_project: { table: "lt_project_bot_memberships", columns: ["tenant_id", "project_id"], unique: false, partial: false },
901
911
  idx_lt_project_bot_memberships_room: { table: "lt_project_bot_memberships", columns: ["tenant_id", "room_id"], unique: false, partial: false },
902
912
  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"] },
903
- 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"] }
913
+ 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"] },
914
+ idx_lt_project_room_read_states_user: { table: "lt_project_room_read_states", columns: ["tenant_id", "user_id"], unique: false, partial: false }
904
915
  };
905
916
  var uniqueConstraints = {
906
917
  lt_project_rooms: [["tenant_id", "id"], ["tenant_id", "project_id", "type"]],
907
918
  lt_project_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "user_id"]],
908
919
  lt_project_bot_memberships: [["tenant_id", "id"], ["tenant_id", "project_id", "assistant_id"]],
909
- lt_project_room_messages: [["tenant_id", "id"]]
920
+ lt_project_room_messages: [["tenant_id", "id"]],
921
+ lt_project_room_read_states: [["tenant_id", "room_id", "user_id"]]
910
922
  };
911
923
  var checkConstraints = {
912
924
  lt_project_rooms: [{ name: "type", fragment: "type='main'" }],
@@ -918,7 +930,8 @@ var checkConstraints = {
918
930
  { name: "role", fragment: "role in ('coordinator','specialist')" },
919
931
  { name: "status", fragment: "status in ('active','paused','removed')" }
920
932
  ],
921
- lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }]
933
+ lt_project_room_messages: [{ name: "source", fragment: "source in ('user','agent','task','routine','system')" }],
934
+ lt_project_room_read_states: []
922
935
  };
923
936
  function rows(db, sql) {
924
937
  const result = db.getRawDb().exec(sql);
@@ -990,10 +1003,10 @@ function validateProjectRoomSchema(db) {
990
1003
  }
991
1004
  for (const [name, expected] of Object.entries(indexes)) {
992
1005
  const listed = rows(db, `PRAGMA index_list(${expected.table})`).find((index) => index.name === name);
993
- const columns5 = rows(db, `PRAGMA index_info(${name})`).map((column) => column.name);
1006
+ const columns6 = rows(db, `PRAGMA index_info(${name})`).map((column) => column.name);
994
1007
  const definition = rows(db, `SELECT sql FROM sqlite_master WHERE type = 'index' AND name = '${name}'`)[0]?.sql;
995
1008
  const normalized = typeof definition === "string" ? definition.toLowerCase().replace(/\s+/g, " ") : "";
996
- if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) || columns5.join(",") !== expected.columns.join(",") || expected.sql?.some((fragment) => !normalized.includes(fragment))) {
1009
+ if (!listed || listed.unique !== Number(expected.unique) || listed.partial !== Number(expected.partial) || columns6.join(",") !== expected.columns.join(",") || expected.sql?.some((fragment) => !normalized.includes(fragment))) {
997
1010
  throw new Error(`Invalid project room schema: index '${name}' is incompatible`);
998
1011
  }
999
1012
  }
@@ -1006,6 +1019,30 @@ function ensureProjectRoomSchema(db) {
1006
1019
  const applied = hasMigrationTable ? rows(db, `SELECT name, version FROM lattice_schema_migrations WHERE store_name = 'project_room' AND name = '${migrationName}'`)[0] : void 0;
1007
1020
  if (applied) {
1008
1021
  if (applied.version !== 1) throw new Error(`Invalid project room schema migration version: ${String(applied.version)}`);
1022
+ const hasReadStates = rows(db, "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'lt_project_room_read_states'").length > 0;
1023
+ if (!hasReadStates) {
1024
+ raw.exec("BEGIN IMMEDIATE");
1025
+ try {
1026
+ raw.exec(`CREATE TABLE IF NOT EXISTS lt_project_room_read_states (
1027
+ tenant_id TEXT NOT NULL, room_id TEXT NOT NULL, user_id TEXT NOT NULL,
1028
+ last_read_at TEXT NOT NULL, updated_at TEXT NOT NULL,
1029
+ PRIMARY KEY (tenant_id, room_id, user_id)
1030
+ ); CREATE INDEX IF NOT EXISTS idx_lt_project_room_read_states_user
1031
+ ON lt_project_room_read_states (tenant_id, user_id);`);
1032
+ raw.run(
1033
+ "INSERT INTO lattice_schema_migrations (store_name, name, version, applied_at) VALUES (?, ?, ?, ?)",
1034
+ ["project_room", "create_project_room_read_states", 2, (/* @__PURE__ */ new Date()).toISOString()]
1035
+ );
1036
+ raw.exec("COMMIT");
1037
+ db.save();
1038
+ } catch (error) {
1039
+ try {
1040
+ raw.exec("ROLLBACK");
1041
+ } catch {
1042
+ }
1043
+ throw error;
1044
+ }
1045
+ }
1009
1046
  validateProjectRoomSchema(db);
1010
1047
  marked[schemaMarker] = true;
1011
1048
  return;
@@ -1424,6 +1461,22 @@ function mapRow4(row) {
1424
1461
  createdAt
1425
1462
  };
1426
1463
  }
1464
+ function mapRowsTolerant(rows2, scope) {
1465
+ const messages = [];
1466
+ for (const row of rows2) {
1467
+ try {
1468
+ messages.push(mapRow4(row));
1469
+ } catch (error) {
1470
+ console.warn("[ProjectRoomMessageStore] Skipping invalid message row", {
1471
+ tenantId: scope.tenantId,
1472
+ roomId: scope.roomId,
1473
+ rowId: typeof row.id === "string" ? row.id : void 0,
1474
+ reason: error instanceof Error ? error.message : String(error)
1475
+ });
1476
+ }
1477
+ }
1478
+ return messages;
1479
+ }
1427
1480
  var LocalProjectRoomMessageStore = class {
1428
1481
  constructor(db) {
1429
1482
  this.db = db;
@@ -1439,12 +1492,24 @@ var LocalProjectRoomMessageStore = class {
1439
1492
  if (input.before && !validDate(input.before.createdAt)) throw new RangeError("Project room message cursor date is invalid");
1440
1493
  const limit = Math.min(100, Math.max(1, Math.trunc(Number.isFinite(input.limit) ? input.limit : 1)));
1441
1494
  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);
1442
- return rows2.map(mapRow4);
1495
+ return mapRowsTolerant(rows2, { tenantId: input.tenantId, roomId: input.roomId });
1443
1496
  }
1444
1497
  async findById(tenantId, id) {
1445
1498
  const row = this.byId(tenantId, id);
1446
1499
  return row ? mapRow4(row) : null;
1447
1500
  }
1501
+ async countAfter(input) {
1502
+ if (!validDate(input.after)) throw new RangeError("Project room message count horizon date is invalid");
1503
+ const rows2 = this.db.prepare(
1504
+ `SELECT author FROM lt_project_room_messages
1505
+ WHERE tenant_id = ? AND room_id = ? AND created_at > ?`
1506
+ ).all(input.tenantId, input.roomId, input.after.toISOString());
1507
+ return rows2.filter((row) => {
1508
+ if (input.excludeAuthorUserId === void 0) return true;
1509
+ const parsed = parseJson(row.author);
1510
+ return !(record(parsed) && parsed.type === "human" && parsed.userId === input.excludeAuthorUserId);
1511
+ }).length;
1512
+ }
1448
1513
  insert(input, idempotent) {
1449
1514
  return transaction(this.db, () => {
1450
1515
  if (idempotent && input.idempotencyKey) {
@@ -1475,6 +1540,50 @@ var LocalProjectRoomMessageStore = class {
1475
1540
  }
1476
1541
  };
1477
1542
 
1543
+ // src/stores/LocalProjectRoomReadStateStore.ts
1544
+ var columns5 = "tenant_id, room_id, user_id, last_read_at, updated_at";
1545
+ function mapRow5(row) {
1546
+ const lastReadAt = new Date(row.last_read_at);
1547
+ const updatedAt = new Date(row.updated_at);
1548
+ if (Number.isNaN(lastReadAt.getTime()) || Number.isNaN(updatedAt.getTime())) throw new Error("Invalid project room read state row");
1549
+ return { tenantId: row.tenant_id, roomId: row.room_id, userId: row.user_id, lastReadAt, updatedAt };
1550
+ }
1551
+ var LocalProjectRoomReadStateStore = class {
1552
+ /** Creates a store over a managed local database wrapper. */
1553
+ constructor(db) {
1554
+ this.db = db;
1555
+ ensureProjectRoomSchema(db);
1556
+ }
1557
+ /** Returns the user's read marker for a room, or null when never reported. */
1558
+ async get(tenantId, roomId, userId) {
1559
+ const row = this.byKey(tenantId, roomId, userId);
1560
+ return row ? mapRow5(row) : null;
1561
+ }
1562
+ /** Upserts the marker monotonically (an older report never regresses the horizon). */
1563
+ async markRead(input) {
1564
+ if (!validDate(input.lastReadAt)) throw new RangeError("Project room read state lastReadAt is invalid");
1565
+ return transaction(this.db, () => {
1566
+ const existing = this.byKey(input.tenantId, input.roomId, input.userId);
1567
+ const lastReadAt = existing !== void 0 && new Date(existing.last_read_at).getTime() > input.lastReadAt.getTime() ? new Date(existing.last_read_at) : input.lastReadAt;
1568
+ write(
1569
+ this.db,
1570
+ `INSERT INTO lt_project_room_read_states (${columns5})
1571
+ VALUES (?, ?, ?, ?, ?)
1572
+ ON CONFLICT (tenant_id, room_id, user_id) DO UPDATE
1573
+ SET last_read_at = excluded.last_read_at, updated_at = excluded.updated_at`,
1574
+ [input.tenantId, input.roomId, input.userId, lastReadAt.toISOString(), timestamp()]
1575
+ );
1576
+ const row = this.byKey(input.tenantId, input.roomId, input.userId);
1577
+ return mapRow5(row);
1578
+ });
1579
+ }
1580
+ byKey(tenantId, roomId, userId) {
1581
+ return this.db.prepare(
1582
+ `SELECT ${columns5} FROM lt_project_room_read_states WHERE tenant_id = ? AND room_id = ? AND user_id = ?`
1583
+ ).get(tenantId, roomId, userId);
1584
+ }
1585
+ };
1586
+
1478
1587
  // src/stores/LocalUserStore.ts
1479
1588
  var DDL5 = `
1480
1589
  CREATE TABLE IF NOT EXISTS lt_users (
@@ -2302,6 +2411,18 @@ function mapRowToEntry3(row) {
2302
2411
  throw new Error("Failed to decrypt MCP server configuration");
2303
2412
  }
2304
2413
  }
2414
+ if (config.headers && row.is_env_encrypted) {
2415
+ try {
2416
+ const decryptedHeaders = {};
2417
+ for (const [key, value] of Object.entries(config.headers)) {
2418
+ decryptedHeaders[key] = (0, import_core2.decrypt)(value);
2419
+ }
2420
+ config.headers = decryptedHeaders;
2421
+ } catch (error) {
2422
+ console.error("Failed to decrypt MCP server headers:", error);
2423
+ throw new Error("Failed to decrypt MCP server configuration");
2424
+ }
2425
+ }
2305
2426
  return {
2306
2427
  id: row.id,
2307
2428
  tenantId: row.tenant_id,
@@ -2327,6 +2448,14 @@ function encryptEnvInConfig(config) {
2327
2448
  configCopy.env = encryptedEnv;
2328
2449
  isEnvEncrypted = true;
2329
2450
  }
2451
+ if (configCopy.headers && Object.keys(configCopy.headers).length > 0) {
2452
+ const encryptedHeaders = {};
2453
+ for (const [key, value] of Object.entries(configCopy.headers)) {
2454
+ encryptedHeaders[key] = (0, import_core2.encrypt)(value);
2455
+ }
2456
+ configCopy.headers = encryptedHeaders;
2457
+ isEnvEncrypted = true;
2458
+ }
2330
2459
  return { config: configCopy, isEnvEncrypted };
2331
2460
  }
2332
2461
 
@@ -3636,7 +3765,7 @@ var LocalChannelInstallationStore = class {
3636
3765
  const row = this.db.prepare(
3637
3766
  `SELECT * FROM lt_channel_installations WHERE id = ?`
3638
3767
  ).get(installationId);
3639
- return row ? mapRow5(row) : null;
3768
+ return row ? mapRow6(row) : null;
3640
3769
  }
3641
3770
  async getInstallationsByTenant(tenantId, channel) {
3642
3771
  let rows2;
@@ -3649,7 +3778,7 @@ var LocalChannelInstallationStore = class {
3649
3778
  `SELECT * FROM lt_channel_installations WHERE tenant_id = ? ORDER BY created_at DESC`
3650
3779
  ).all(tenantId);
3651
3780
  }
3652
- return rows2.map(mapRow5);
3781
+ return rows2.map(mapRow6);
3653
3782
  }
3654
3783
  async getAllInstallations(channel) {
3655
3784
  let rows2;
@@ -3662,7 +3791,7 @@ var LocalChannelInstallationStore = class {
3662
3791
  `SELECT * FROM lt_channel_installations ORDER BY created_at DESC`
3663
3792
  ).all();
3664
3793
  }
3665
- return rows2.map(mapRow5);
3794
+ return rows2.map(mapRow6);
3666
3795
  }
3667
3796
  async createInstallation(tenantId, installationId, data) {
3668
3797
  const now = nowISO();
@@ -3738,7 +3867,7 @@ var LocalChannelInstallationStore = class {
3738
3867
  return result.changes > 0;
3739
3868
  }
3740
3869
  };
3741
- function mapRow5(row) {
3870
+ function mapRow6(row) {
3742
3871
  return {
3743
3872
  id: row.id,
3744
3873
  tenantId: row.tenant_id,
@@ -3763,6 +3892,7 @@ CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
3763
3892
  tenant_id TEXT NOT NULL,
3764
3893
  project_id TEXT,
3765
3894
  assistant_ids TEXT,
3895
+ grants TEXT,
3766
3896
  label TEXT,
3767
3897
  enabled INTEGER NOT NULL DEFAULT 1,
3768
3898
  created_at TEXT NOT NULL,
@@ -3772,6 +3902,15 @@ CREATE TABLE IF NOT EXISTS lt_a2a_api_keys (
3772
3902
  function generateApiKey() {
3773
3903
  return `a2a_${(0, import_crypto3.randomUUID)().replace(/-/g, "")}`;
3774
3904
  }
3905
+ function parseGrants(raw) {
3906
+ if (!raw) return void 0;
3907
+ try {
3908
+ const parsed = JSON.parse(raw);
3909
+ return Array.isArray(parsed) ? parsed : void 0;
3910
+ } catch {
3911
+ return void 0;
3912
+ }
3913
+ }
3775
3914
  function parseAssistantIds(raw) {
3776
3915
  if (!raw) return void 0;
3777
3916
  try {
@@ -3786,6 +3925,7 @@ var LocalA2AApiKeyStore = class {
3786
3925
  this.db = db;
3787
3926
  ensureTable(db, DDL16);
3788
3927
  this.ensureColumn("lt_a2a_api_keys", "assistant_ids", "assistant_ids TEXT");
3928
+ this.ensureColumn("lt_a2a_api_keys", "grants", "grants TEXT");
3789
3929
  this.repairManagementIds();
3790
3930
  }
3791
3931
  /** Add a column if it does not exist (SQLite version compatible). */
@@ -3846,9 +3986,9 @@ var LocalA2AApiKeyStore = class {
3846
3986
  const key = generateApiKey();
3847
3987
  const now = nowISO();
3848
3988
  this.db.prepare(
3849
- `INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, label, created_at, updated_at)
3850
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
3851
- ).run(id, (0, import_core3.encrypt)(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.label || null, now, now);
3989
+ `INSERT INTO lt_a2a_api_keys (id, key_value, tenant_id, project_id, assistant_ids, grants, label, created_at, updated_at)
3990
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
3991
+ ).run(id, (0, import_core3.encrypt)(key), input.tenantId, input.projectId, input.assistantIds ? JSON.stringify(input.assistantIds) : null, input.grants ? JSON.stringify(input.grants) : null, input.label || null, now, now);
3852
3992
  const record2 = {
3853
3993
  id,
3854
3994
  key,
@@ -3880,6 +4020,38 @@ var LocalA2AApiKeyStore = class {
3880
4020
  if (!row) throw new Error(`A2A API key not found: ${id}`);
3881
4021
  return mapRowToRecord(row);
3882
4022
  }
4023
+ async update(id, input) {
4024
+ const sets = [];
4025
+ const values = [];
4026
+ if (input.label !== void 0) {
4027
+ sets.push("label = ?");
4028
+ values.push(input.label || null);
4029
+ }
4030
+ if (input.projectId !== void 0) {
4031
+ sets.push("project_id = ?");
4032
+ values.push(input.projectId);
4033
+ }
4034
+ if (input.assistantIds !== void 0) {
4035
+ sets.push("assistant_ids = ?");
4036
+ values.push(input.assistantIds ? JSON.stringify(input.assistantIds) : null);
4037
+ }
4038
+ if (input.grants !== void 0) {
4039
+ sets.push("grants = ?");
4040
+ values.push(input.grants ? JSON.stringify(input.grants) : null);
4041
+ }
4042
+ if (sets.length === 0) {
4043
+ const row2 = this.db.prepare("SELECT * FROM lt_a2a_api_keys WHERE id = ?").get(id);
4044
+ if (!row2) throw new Error(`A2A API key not found: ${id}`);
4045
+ return mapRowToRecord(row2);
4046
+ }
4047
+ sets.push("updated_at = ?");
4048
+ values.push(nowISO());
4049
+ values.push(id);
4050
+ this.db.prepare(`UPDATE lt_a2a_api_keys SET ${sets.join(", ")} WHERE id = ?`).run(...values);
4051
+ const row = this.db.prepare("SELECT * FROM lt_a2a_api_keys WHERE id = ?").get(id);
4052
+ if (!row) throw new Error(`A2A API key not found: ${id}`);
4053
+ return mapRowToRecord(row);
4054
+ }
3883
4055
  async rotate(id) {
3884
4056
  const key = generateApiKey();
3885
4057
  const now = nowISO();
@@ -3928,6 +4100,7 @@ function mapRowToRecord(row) {
3928
4100
  tenantId: row.tenant_id,
3929
4101
  projectId: row.project_id,
3930
4102
  assistantIds: parseAssistantIds(row.assistant_ids),
4103
+ grants: parseGrants(row.grants),
3931
4104
  label: row.label || void 0,
3932
4105
  enabled: row.enabled === 1,
3933
4106
  createdAt: parseISO(row.created_at),
@@ -3935,10 +4108,100 @@ function mapRowToRecord(row) {
3935
4108
  };
3936
4109
  }
3937
4110
 
3938
- // src/stores/LocalThreadMessageQueueStore.ts
3939
- var import_protocols4 = require("@axiom-lattice/protocols");
4111
+ // src/stores/LocalOpenAuditStore.ts
3940
4112
  var import_crypto4 = require("crypto");
3941
4113
  var DDL17 = `
4114
+ CREATE TABLE IF NOT EXISTS lt_open_audit (
4115
+ id TEXT PRIMARY KEY,
4116
+ tenant_id TEXT NOT NULL,
4117
+ credential_id TEXT NOT NULL,
4118
+ credential_kind TEXT NOT NULL,
4119
+ project_id TEXT NOT NULL,
4120
+ domain TEXT NOT NULL,
4121
+ action TEXT NOT NULL,
4122
+ args TEXT,
4123
+ status TEXT NOT NULL,
4124
+ error_code TEXT,
4125
+ duration_ms INTEGER NOT NULL,
4126
+ created_at TEXT NOT NULL
4127
+ );
4128
+ `;
4129
+ var LocalOpenAuditStore = class {
4130
+ constructor(db) {
4131
+ this.db = db;
4132
+ ensureTable(db, DDL17);
4133
+ }
4134
+ async query(params) {
4135
+ const clauses = ["tenant_id = ?"];
4136
+ const values = [params.tenantId];
4137
+ if (params.credentialId) {
4138
+ clauses.push("credential_id = ?");
4139
+ values.push(params.credentialId);
4140
+ }
4141
+ if (params.domain) {
4142
+ clauses.push("domain = ?");
4143
+ values.push(params.domain);
4144
+ }
4145
+ if (params.status) {
4146
+ clauses.push("status = ?");
4147
+ values.push(params.status);
4148
+ }
4149
+ if (params.from) {
4150
+ clauses.push("created_at >= ?");
4151
+ values.push(params.from);
4152
+ }
4153
+ if (params.to) {
4154
+ clauses.push("created_at < ?");
4155
+ values.push(params.to);
4156
+ }
4157
+ const limit = params.limit ?? 100;
4158
+ const offset = params.offset ?? 0;
4159
+ values.push(limit, offset);
4160
+ const rows2 = this.db.prepare(
4161
+ `SELECT * FROM lt_open_audit WHERE ${clauses.join(" AND ")}
4162
+ ORDER BY created_at DESC LIMIT ? OFFSET ?`
4163
+ ).all(...values);
4164
+ return rows2.map((row) => ({
4165
+ id: row.id,
4166
+ tenantId: row.tenant_id,
4167
+ credentialId: row.credential_id,
4168
+ credentialKind: row.credential_kind,
4169
+ projectId: row.project_id,
4170
+ domain: row.domain,
4171
+ action: row.action,
4172
+ args: row.args ? JSON.parse(row.args) : void 0,
4173
+ status: row.status,
4174
+ errorCode: row.error_code ?? void 0,
4175
+ durationMs: row.duration_ms,
4176
+ createdAt: parseISO(row.created_at)
4177
+ }));
4178
+ }
4179
+ async append(record2) {
4180
+ this.db.prepare(
4181
+ `INSERT INTO lt_open_audit
4182
+ (id, tenant_id, credential_id, credential_kind, project_id, domain, action, args, status, error_code, duration_ms, created_at)
4183
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
4184
+ ).run(
4185
+ (0, import_crypto4.randomUUID)(),
4186
+ record2.tenantId,
4187
+ record2.credentialId,
4188
+ record2.credentialKind,
4189
+ record2.projectId,
4190
+ record2.domain,
4191
+ record2.action,
4192
+ record2.args === void 0 ? null : JSON.stringify(record2.args),
4193
+ record2.status,
4194
+ record2.errorCode ?? null,
4195
+ Math.max(0, Math.round(record2.durationMs)),
4196
+ nowISO()
4197
+ );
4198
+ }
4199
+ };
4200
+
4201
+ // src/stores/LocalThreadMessageQueueStore.ts
4202
+ var import_protocols4 = require("@axiom-lattice/protocols");
4203
+ var import_crypto5 = require("crypto");
4204
+ var DDL18 = `
3942
4205
  CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
3943
4206
  id TEXT PRIMARY KEY,
3944
4207
  thread_id TEXT NOT NULL,
@@ -3964,9 +4227,9 @@ var LocalThreadMessageQueueStore = class {
3964
4227
  constructor(db) {
3965
4228
  this.capacityTail = Promise.resolve();
3966
4229
  this.db = db;
3967
- ensureTable(db, DDL17);
3968
- const columns5 = db.prepare("PRAGMA table_info(lt_thread_message_queue)").all();
3969
- const names = new Set(columns5.map(({ name }) => name));
4230
+ ensureTable(db, DDL18);
4231
+ const columns6 = db.prepare("PRAGMA table_info(lt_thread_message_queue)").all();
4232
+ const names = new Set(columns6.map(({ name }) => name));
3970
4233
  if (!names.has("trusted_run_context")) {
3971
4234
  db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN trusted_run_context TEXT");
3972
4235
  }
@@ -3977,7 +4240,7 @@ var LocalThreadMessageQueueStore = class {
3977
4240
  async addMessage(params) {
3978
4241
  const trusted = validateQueueTrust(params);
3979
4242
  const now = nowISO();
3980
- const id = params.id || (0, import_crypto4.randomUUID)();
4243
+ const id = params.id || (0, import_crypto5.randomUUID)();
3981
4244
  const seqRow = this.db.prepare(
3982
4245
  `SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
3983
4246
  ).get(params.threadId);
@@ -4020,7 +4283,7 @@ var LocalThreadMessageQueueStore = class {
4020
4283
  projectId: params.projectId
4021
4284
  };
4022
4285
  const filter = scopeClause(scope);
4023
- const id = params.id || (0, import_crypto4.randomUUID)();
4286
+ const id = params.id || (0, import_crypto5.randomUUID)();
4024
4287
  const limit = maxSize === Infinity ? -1 : maxSize;
4025
4288
  const result = this.db.prepare(
4026
4289
  `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)
@@ -4058,7 +4321,7 @@ var LocalThreadMessageQueueStore = class {
4058
4321
  async addMessageAtHead(params) {
4059
4322
  const trusted = validateQueueTrust(params);
4060
4323
  const now = nowISO();
4061
- const id = params.id || (0, import_crypto4.randomUUID)();
4324
+ const id = params.id || (0, import_crypto5.randomUUID)();
4062
4325
  const seqRow = this.db.prepare(
4063
4326
  `SELECT COALESCE(MAX(sequence_order), 0) + 1 as next_seq FROM lt_thread_message_queue WHERE thread_id = ?`
4064
4327
  ).get(params.threadId);
@@ -4177,7 +4440,7 @@ function rowToMessage(row) {
4177
4440
  }
4178
4441
 
4179
4442
  // src/stores/LocalSkillStore.ts
4180
- var DDL18 = `
4443
+ var DDL19 = `
4181
4444
  CREATE TABLE IF NOT EXISTS lt_skills (
4182
4445
  id TEXT NOT NULL,
4183
4446
  tenant_id TEXT NOT NULL,
@@ -4196,7 +4459,7 @@ CREATE TABLE IF NOT EXISTS lt_skills (
4196
4459
  var LocalSkillStore = class {
4197
4460
  constructor(db) {
4198
4461
  this.db = db;
4199
- ensureTable(db, DDL18);
4462
+ ensureTable(db, DDL19);
4200
4463
  }
4201
4464
  async getAllSkills(tenantId, _context) {
4202
4465
  const rows2 = this.db.prepare(
@@ -4346,7 +4609,7 @@ function mapRowToSkill(row) {
4346
4609
  }
4347
4610
 
4348
4611
  // src/stores/LocalScheduleStorage.ts
4349
- var DDL19 = `
4612
+ var DDL20 = `
4350
4613
  CREATE TABLE IF NOT EXISTS lt_scheduled_tasks (
4351
4614
  task_id TEXT PRIMARY KEY,
4352
4615
  task_type TEXT NOT NULL,
@@ -4379,7 +4642,7 @@ CREATE INDEX IF NOT EXISTS idx_lt_st_tenant ON lt_scheduled_tasks(tenant_id);
4379
4642
  var LocalScheduleStorage = class {
4380
4643
  constructor(db) {
4381
4644
  this.db = db;
4382
- ensureTable(db, DDL19);
4645
+ ensureTable(db, DDL20);
4383
4646
  }
4384
4647
  async save(task) {
4385
4648
  this.db.prepare(
@@ -4673,7 +4936,7 @@ function mapRowToTask(row) {
4673
4936
  }
4674
4937
 
4675
4938
  // src/stores/LocalTaskStore.ts
4676
- var import_crypto5 = require("crypto");
4939
+ var import_crypto6 = require("crypto");
4677
4940
  var MAX_CANONICAL_TASK_TIME = "9999-12-31T23:59:59.999Z";
4678
4941
  var TASK_STATUSES = /* @__PURE__ */ new Set([
4679
4942
  "pending",
@@ -4689,7 +4952,7 @@ var NEXT_UPDATED_AT_SQL = `updated_at = CASE
4689
4952
  THEN strftime('%Y-%m-%dT%H:%M:%fZ', updated_at, '+0.001 seconds')
4690
4953
  ELSE ?
4691
4954
  END`;
4692
- var DDL20 = `
4955
+ var DDL21 = `
4693
4956
  CREATE TABLE IF NOT EXISTS lt_tasks (
4694
4957
  id TEXT NOT NULL,
4695
4958
  tenant_id TEXT NOT NULL,
@@ -4762,7 +5025,7 @@ function assertPage(limit, offset = 0) {
4762
5025
  var LocalTaskStore = class {
4763
5026
  constructor(db) {
4764
5027
  this.db = db;
4765
- ensureTable(db, DDL20);
5028
+ ensureTable(db, DDL21);
4766
5029
  this.ensureColumn("lt_tasks", "require_review", "require_review INTEGER DEFAULT 0");
4767
5030
  this.ensureColumn("lt_tasks", "dependencies", "dependencies TEXT");
4768
5031
  this.ensureColumn("lt_tasks", "result", "result TEXT");
@@ -4779,7 +5042,7 @@ var LocalTaskStore = class {
4779
5042
  }
4780
5043
  }
4781
5044
  async create(params) {
4782
- const id = params.id ?? (0, import_crypto5.randomUUID)();
5045
+ const id = params.id ?? (0, import_crypto6.randomUUID)();
4783
5046
  const now = nowISO();
4784
5047
  this.db.prepare(
4785
5048
  `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)
@@ -5439,7 +5702,7 @@ var PROJECT_LIFECYCLE_ACTIONS = /* @__PURE__ */ new Set([
5439
5702
  "cancelled",
5440
5703
  "reassigned"
5441
5704
  ]);
5442
- var DDL21 = `
5705
+ var DDL22 = `
5443
5706
  CREATE TABLE IF NOT EXISTS lt_task_work_items (
5444
5707
  id TEXT NOT NULL,
5445
5708
  tenant_id TEXT NOT NULL,
@@ -5513,7 +5776,7 @@ function assertLifecyclePage(query) {
5513
5776
  var LocalTaskWorkItemStore = class {
5514
5777
  constructor(db) {
5515
5778
  this.db = db;
5516
- ensureTable(db, DDL21);
5779
+ ensureTable(db, DDL22);
5517
5780
  this.ensureColumn("lt_task_work_items", "workspace_id", "workspace_id TEXT");
5518
5781
  this.ensureColumn("lt_task_work_items", "project_id", "project_id TEXT");
5519
5782
  this.ensureColumn("lt_task_work_items", "event_key", "event_key TEXT");
@@ -5709,8 +5972,8 @@ var LocalTaskWorkItemStore = class {
5709
5972
  };
5710
5973
 
5711
5974
  // src/stores/LocalCapabilityBundleStore.ts
5712
- var import_crypto6 = require("crypto");
5713
- 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);`;
5975
+ var import_crypto7 = require("crypto");
5976
+ 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);`;
5714
5977
  var duplicateMessage = "Capability bundle key already exists for tenant";
5715
5978
  function map(row) {
5716
5979
  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() };
@@ -5731,7 +5994,7 @@ var LocalCapabilityBundleStore = class {
5731
5994
  */
5732
5995
  constructor(db) {
5733
5996
  this.db = db;
5734
- ensureTable(db, DDL22);
5997
+ ensureTable(db, DDL23);
5735
5998
  }
5736
5999
  async listByTenant(tenantId) {
5737
6000
  return this.db.prepare("SELECT * FROM lt_capability_bundles WHERE tenant_id = ? ORDER BY created_at").all(tenantId).map(map);
@@ -5748,7 +6011,7 @@ var LocalCapabilityBundleStore = class {
5748
6011
  }
5749
6012
  async create(tenantId, input) {
5750
6013
  const now = nextTimestamp();
5751
- const id = (0, import_crypto6.randomUUID)();
6014
+ const id = (0, import_crypto7.randomUUID)();
5752
6015
  try {
5753
6016
  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);
5754
6017
  } catch (error) {
@@ -5817,6 +6080,7 @@ async function createLocalStoreConfig(options = {}) {
5817
6080
  projectMembership: new LocalProjectMembershipStore(db),
5818
6081
  projectBotMembership: new LocalProjectBotMembershipStore(db),
5819
6082
  projectRoomMessage: new LocalProjectRoomMessageStore(db),
6083
+ projectRoomReadState: new LocalProjectRoomReadStateStore(db),
5820
6084
  user: new LocalUserStore(db),
5821
6085
  tenant: new LocalTenantStore(db),
5822
6086
  userTenantLink: new LocalUserTenantLinkStore(db),
@@ -5829,6 +6093,7 @@ async function createLocalStoreConfig(options = {}) {
5829
6093
  channelBinding: new LocalChannelBindingStore(db),
5830
6094
  channelInstallation: new LocalChannelInstallationStore(db),
5831
6095
  a2aApiKey: new LocalA2AApiKeyStore(db),
6096
+ openAudit: new LocalOpenAuditStore(db),
5832
6097
  threadMessageQueue: new LocalThreadMessageQueueStore(db),
5833
6098
  skill: new LocalSkillStore(db),
5834
6099
  schedule: new LocalScheduleStorage(db),
@@ -5891,9 +6156,11 @@ var InMemoryConversationStore = class {
5891
6156
  LocalEvalStore,
5892
6157
  LocalMcpServerConfigStore,
5893
6158
  LocalMetricsServerConfigStore,
6159
+ LocalOpenAuditStore,
5894
6160
  LocalProjectBotMembershipStore,
5895
6161
  LocalProjectMembershipStore,
5896
6162
  LocalProjectRoomMessageStore,
6163
+ LocalProjectRoomReadStateStore,
5897
6164
  LocalProjectRoomStore,
5898
6165
  LocalProjectStore,
5899
6166
  LocalScheduleStorage,